/*
 * Copyright (c) 2009, Jan Pastor. All Rights Reserved.
 */

package sk.jp.commons.ui.bgproc;

import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import javax.swing.*;

import sk.jp.commons.utils.BackgroundProcess;
import sk.jp.commons.utils.ExceptionUtils;
import sk.jp.commons.tools.Messages;
import sk.jp.commons.exception.ApplicationException;
import sk.jp.commons.ui.QuestionDialog;
import sk.jp.commons.ui.InfoDialog;
import sk.jp.commons.ui.StatusBar;

/**
 * Component that present state of process running asynchronously on background.
 * <p>This component is designed to be placed on {@link StatusBar}.
 * <p>See package description for additional information.
 */
public class BackgroundProcessIndicator extends JLabel
                                        implements BackgroundProcess.StateHandler {

    public enum Msg {
        dlgTitle("ui.backgrounprocessindicator.dialog.title"),//Background process
        running("ui.backgrounprocessindicator.running"),//Running:
        btnInfoClose("ui.backgrounprocessindicator.dialog.btn.close"),//OK
        btnQuestClose("ui.backgrounprocessindicator.dialog.btn.close"),//Close
        btnQuestCancelproc("ui.backgrounprocessindicator.dialog.btn.cancelproc"),//Cancel process
        idle("ui.backgrounprocessindicator.idle"),//No running background process
        cancelling("ui.backgrounprocessindicator.cancelling"),//Cancelling: 
        ;
        //----------------------------------------------------------------------
        private String propKey;
        Msg(String propKey) {
            this.propKey = propKey;
        }
        public String getPropKey() {
            return propKey;
        }
        public String value() {
            return Messages.getMessage(propKey);
        }
    }

    //----------------------------------------------------------------------

    private final ImageIcon workingIcon;
    private final ImageIcon cancellingIcon;
    private final ImageIcon idleIcon;
    private BackgroundProcess backgroundProcess;
    private boolean processRunning;

    //----------------------------------------------------------------------

    /**
     * Initialize instance with presentation icons.
     * @param workingIcon icon for present running process. Animated icon is receommended.
     * @param cancellingIcon icon for present running but cancelling process.
     * Animated icon is receommended.
     * @param idleIcon icon for present idle process (nothing running). Static gray-style
     * icon is receommended.
     */
    public BackgroundProcessIndicator(ImageIcon workingIcon, ImageIcon cancellingIcon,
                                      ImageIcon idleIcon) {
        this.workingIcon = workingIcon;
        this.cancellingIcon = cancellingIcon;
        this.idleIcon = idleIcon;

        processDone();
    }

    //----------------------------------------------------------------------

    /**
     * Starts given process on background.
     * <p>If this indicator already starts (currently running) process, throws
     * exception - it doesn't supports multiple processes to manage.
     * @param backgroundProcess
     * @param mouseListener mouse listener for handling mouse events (click) on
     * this indicator component - individually for every started process, or
     * you can use predefined {@link DefaultClickHandler}.
     */
    public synchronized void start(BackgroundProcess backgroundProcess,
                                   MouseListener mouseListener){
        if(isProcessRunning()){
            throw new ApplicationException("Can't start new background process managed by this indicator, because there is already running process",
                                           "ui.backgrounprocessindicator.err.cantstart",
                                           ExceptionUtils.paramsToMap(
                                               "runningProcess", this.backgroundProcess.getInfo(),
                                               "processToRun", backgroundProcess.getInfo()));
        }

        this.backgroundProcess = backgroundProcess;
        processRunning = true;
        if(mouseListener != null){
            addMouseListener(mouseListener);
        }

        backgroundProcess.start();
        setIcon(workingIcon);
        setToolTipText(Msg.running.value() + backgroundProcess.getInfo());
    }

    /**
     * Returns true if process managed by this indicator is currently running.
     * @return true if process managed by this indicator is currently running, false
     * if not.
     */
    public synchronized boolean isProcessRunning() {
        return processRunning;
    }

    /**
     * Cancell immediately (make request for) currently running background process.
     * @return false if there is not a running process or currently running
     * process is not cancellable; else returns true.
     */
    public synchronized boolean cancelRunningProcess(){
        if(!isProcessRunning() || !backgroundProcess.isCancellable()){
            return false;
        }
        boolean r = backgroundProcess.cancel();
        setIcon(cancellingIcon);
        setToolTipText(Msg.cancelling.value() + backgroundProcess.getInfo());
        return r;
    }

    //----------------------------------------------------------------------

    public synchronized final void processStarted(String processInfo) {
    }

    public synchronized final void processCancelled(String processInfo) {
        processDone();
    }

    public synchronized final void processFinished(String processInfo) {
        processDone();
    }

    public synchronized final void processFinishedWithError(String processInfo, Throwable t) {
        processDone();
    }

    private synchronized void processDone(){
        setIcon(idleIcon);
        //setToolTipText(Msg.idle.value());

        MouseListener[] listeners = getMouseListeners();
        for (MouseListener listener : listeners) {
            removeMouseListener(listener);
        }

        processRunning = false;
        backgroundProcess = null;
    }

    //----------------------------------------------------------------------

    /**
     * Predefined click handler that shows question with choice to cancel process,
     * or info dialog about running process.
     */
    public static class DefaultClickHandler extends MouseAdapter {
        private final BackgroundProcess backgroundProcess;
        private final BackgroundProcessIndicator backgroundProcessIndicator;

        /**
         * Initialize mouse listener.
         * @param backgroundProcess 
         * @param backgroundProcessIndicator
         */
        public DefaultClickHandler(BackgroundProcess backgroundProcess,
                                   BackgroundProcessIndicator backgroundProcessIndicator) {
            this.backgroundProcess = backgroundProcess;
            this.backgroundProcessIndicator = backgroundProcessIndicator;
        }

        @Override
        public void mouseClicked(MouseEvent e) {
            super.mouseClicked(e);
            if(backgroundProcess.isCancellable()){
                QuestionDialog dialog = new QuestionDialog(Msg.dlgTitle.value(),
                                               new String[]{Msg.btnQuestCancelproc.value(),
                                                            Msg.btnQuestClose.value()},
                                               Msg.btnQuestClose.value());
                String q = Msg.running.value() + " " + backgroundProcess.getInfo();
                String r = dialog.show(null, q);
                if(Msg.btnQuestCancelproc.value().equals(r)){
                    backgroundProcessIndicator.cancelRunningProcess();
                }
            }else{
                new InfoDialog(Msg.dlgTitle.value(), Msg.btnInfoClose.value())
                    .show(null, Msg.running.value() + " " + backgroundProcess.getInfo());
            }
        }
    }

}
